package top.rainbowecho.common.validate;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 进行参数校验的切面拦截
 *
 * @author rainbow
 * @since 2019/11/23 14:14
 */
@Component
@Aspect
public class ValidateInterceptor {
    private ParamValidator paramValidator;

    @Autowired
    public void setParamValidator(ParamValidator paramValidator) {
        this.paramValidator = paramValidator;
    }

    @Pointcut("execution(public * *(..))"
            + " && !execution(String *.toString())"
            + " && !execution(int *.hashCode())"
            + " && !execution(boolean *.canEqual(Object))"
            + " && !execution(boolean *.equals(Object))")
    protected void publicMethod() {
    }

    @Pointcut("execution(* *(.., @top.rainbowecho.common.validate.Valid (*), ..))")
    protected void paramValidatePointCut() {}

    @Pointcut("@annotation(top.rainbowecho.common.validate.Valid)")
    protected void methodValidatePointCut() {}

    /**
     * 对访问权限是public并且参数被@Valid注解修饰的方法进行拦截校验
     *
     * @param jp 连接点
     * @return 连接点的执行结果
     * @throws Throwable 连接点执行失败
     */
    @Around("publicMethod() && paramValidatePointCut()")
    public Object paramLevelValidate(ProceedingJoinPoint jp) throws Throwable {
        return commonValidate((params, args) -> {
            for (int i = 0; i < params.length; i++) {
                Valid annotation = params[i].getAnnotation(Valid.class);
                if (annotation != null) {
                    paramValidator.fastFailValidate(args[i]);
                }
            }
        }, jp);
    }

    /**
     * 对访问权限是public并且被@Valid注解修饰的方法进行拦截校验
     *
     * @param jp 连接点
     * @return 连接点的执行结果
     * @throws Throwable 连接点执行失败
     */
    @Around("publicMethod() && methodValidatePointCut()")
    public Object methodLevelValidate(ProceedingJoinPoint jp) throws Throwable {
        return commonValidate((params, args) -> {
            for (int i = 0; i < params.length; i++) {
                paramValidator.fastFailValidate(args[i]);
            }
        }, jp);
    }

    /**
     * 参数校验逻辑逻辑
     *
     * @param jp 连接点
     * @param validate lambda形式的校验逻辑，根据拦截级别不同传入不同的逻辑代码
     * @return 连接点的执行结果
     * @throws Throwable 连接点执行失败
     */
    private Object commonValidate(CommonValidate validate, ProceedingJoinPoint jp) throws Throwable {
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();

        // 获取参数及参数值
        Parameter[] parameters = method.getParameters();
        Object[] args = jp.getArgs();

        validate.validate(parameters, args);

        return jp.proceed();
    }
}
